/*-
 * Copyright (C) 2018-2020, Shanghai Trusted Industry Control Platform Co., Ltd.
 * Created by 周正辉 (Zhenghui Zhou) <zhouzhenghui@ticpsh.com>, 赵楷文 (Kaiwen
 * Zhao) <zhaokaiwen@ticpsh.com>
 */

#ifndef TESTIF_H
#define TESTIF_H

#include <clang/AST/ASTConsumer.h>
#include <clang/AST/ASTContext.h>
#include <clang/AST/DeclBase.h>
#include <clang/AST/ExprCXX.h>
#include <clang/AST/QualTypeNames.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Analysis/AnalysisDeclContext.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/TextDiagnosticBuffer.h>
#include <clang/Lex/Lexer.h>
#include <clang/Parse/Parser.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <clang/StaticAnalyzer/Core/AnalyzerOptions.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/ADT/APFloat.h>
#include <llvm/Support/raw_ostream.h>

#include <iomanip>
#include <iostream>
#include <map>
#include <nlohmann/json.hpp>
#include <set>
#include <sstream>

#include "common/AttrAnalyzer.h"
#include "share/analyze_setting.h"
#include "share/fixed_address.h"
#include "testhelper.h"
#include "testvisitor.h"

namespace rocket {
namespace testif {

/**
 * @brief 作为AST消费者插桩类
 * 作为观察者模式类继承自chang::ASTConsumer
 */
class TestIfConsumer : public clang::ASTConsumer,
                       public clang::tooling::SourceFileCallbacks {
 public:
  clang::CompilerInstance *CI;
  const share::RocketAnalyzeSetting &setting;

 protected:
  nlohmann::json &types_j;
  nlohmann::json &literals_j;
  nlohmann::json &globals_j;
  nlohmann::json &functions_j;
  nlohmann::json &fixed_addresses_j;
  //  nlohmann::json &functiondecls_j;
  nlohmann::json &macros_j;
  nlohmann::json &codelines_j;

  std::map<const clang::FunctionDecl *, FuncDeclInfo>
      function_declarations; /**< 函数声明*/

  std::map<clang::VarDecl *, TargetGlobalVar>
      global_declarations; /**< 全局变量*/
  std::map<clang::FunctionDecl *, TargetFunction>
      function_definitions; /**< 待测函数*/
  std::map<clang::FunctionDecl *, TargetFunction>::iterator
      function_def_it; /**< 待测函数迭代器*/
  std::map<uint64_t, std::multimap<std::string, share::FixedAddress>>
      fixedaddr_bases; /**< 绝对地址基地址*/

  CodeLineInfo codeline_info;

  std::map<clang::FileID, std::set<std::string>> file2types_map;
  std::map<nlohmann::json *, std::set<clang::FileID>> type2files_map;

  std::vector<clang::FileID> current_file;

 public:
  TestIfConsumer(const share::RocketAnalyzeSetting &setting,
                 nlohmann::json &types_j, nlohmann::json &literals_j,
                 nlohmann::json &globals_j, nlohmann::json &functions_j,
                 nlohmann::json &fixed_addresses_j,
                 /*nlohmann::json &functiondecls_j,*/ nlohmann::json &macros_j,
                 nlohmann::json &codelines_j)
      : setting(setting),
        types_j(types_j),
        literals_j(literals_j),
        globals_j(globals_j),
        functions_j(functions_j),
        fixed_addresses_j(fixed_addresses_j),
        //        functiondecls_j(functiondecls_j),
        macros_j(macros_j),
        codelines_j(codelines_j) {}

  // 编译实例设置
  bool handleBeginSource(clang::CompilerInstance &CI_) override {
    CI = &CI_;
    CI->getPreprocessorPtr()->enableIncrementalProcessing();
    return true;
  }

  void Initialize(clang::ASTContext &) override;

  /**
   * @brief 从顶层声明中找出待测函数并根据测试用例生成相应main函数和桩函数
   * 继承自clang::ASTConsumer并作为观察者模式接口被调用
   * @see clang::ASTConsumer::HandleTopLevelDecl()
   */
  //  virtual bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
  virtual void HandleTranslationUnit(clang::ASTContext &ast) override;

  bool isCXX() const { return CI->getASTContext().getLangOpts().CPlusPlus; }
  bool isInExcludeNamespace(const clang::Decl *decl);

  std::string getFullIFTypeName(const clang::QualType &qt,
                                clang::ASTContext &ast);
  std::string currectKeyString(std::string str);

 protected:
  /**
   * @brief 类型/函数/全局变量/字面量在写入报文之前的同意操作接口
   */
  virtual void elementCallBack(nlohmann::json &) {}

 public:
  virtual nlohmann::json parse_variable(clang::VarDecl *vd,
                                        bool visit_definition = true);

  virtual std::string parse_cxxrecord_type(const clang::CXXRecordDecl *cxxrd);

  /**
   * @brief 将某一个类型转换为相应的nlohmann::json报文
   * @return 返回解析得到的类型名
   */
  virtual std::string parse_type(const clang::QualType &type_);

  // *************************************************************************
  // 全局变量及其初始化和字面量的解析接口

  /**
   * @brief 解析初始化表达式
   */
  virtual nlohmann::json parse_init_expr(const clang::QualType &type_,
                                         const clang::Expr *init);
  /**
   * @brief 解析列表初始化表达式
   */
  virtual nlohmann::json parse_init_list_expr(const clang::QualType &type,
                                              const clang::InitListExpr *ile);
  /**
   * @brief 解析值
   * APValue - This class implements a discriminated union of [uninitialized]
   * [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
   * [Vector: N * APValue], [Array: N * APValue].
   */
  virtual nlohmann::json parse_apvalue(const clang::QualType &type,
                                       const clang::APValue &apvalue);

  /**
   * @brief 解析模板形参
   */
  virtual nlohmann::json parse_template_args(
      const clang::TemplateArgumentList *ta_list);

  /**
   * @brief 解析字符串字面量初始化
   */
  virtual nlohmann::json parse_init_string_literal(
      const clang::QualType &type, const clang::StringLiteral *sl,
      const std::string &type_name);

  /**
   * @brief 解析用户自定义字面量初始化
   */
  virtual std::string parse_init_udliteral_expr(clang::UserDefinedLiteral *udl);
  /**
   * @brief 解析结构体的列表初始化
   */
  virtual nlohmann::json parse_init_list_struct(const clang::RecordDecl *record,
                                                const clang::InitListExpr *ile);
  /**
   * @brief 解析联合体列表初始化
   */
  virtual nlohmann::json parse_init_list_union(const clang::InitListExpr *ile);
  /**
   * @brief 解析数组列表初始化
   */
  virtual nlohmann::json parse_init_list_array(const clang::ArrayType *type,
                                               const clang::InitListExpr *ile);

  virtual nlohmann::json parse_apvalue_array(const clang::ArrayType *array_type,
                                             const clang::APValue &apvalue);

  virtual nlohmann::json parse_apvalue_struct(const clang::RecordDecl *record,
                                              const clang::APValue &apvalue);

  virtual nlohmann::json parse_apvalue_union(const clang::APValue &apvalue);

  virtual nlohmann::json parse_apvalue_member_pointer(
      const clang::APValue &apvalue);

  // *************************************************************************
  // 类型与函数源码解析

  /**
   * @brief parse_scopes
   * 解析边界作用域
   * @param info
   * @return
   */
  virtual nlohmann::json parse_scopes(const clang::DeclContext *const dc,
                                      bool &hasFuncScope,
                                      bool &hasAnonymousScope);

  virtual nlohmann::json parse_scopes(const clang::DeclContext *const dc);

  virtual std::string parse_template_decl(const clang::TemplateDecl *td);

  /**
   * @brief parse_template_arg
   * 解析单个模板实参
   * @param ob
   * @param ta
   * @param ast
   * @return
   */
  virtual nlohmann::json parse_template_arg(const clang::TemplateArgument &ta);

  /**
   * @brief parse_template_parms
   * 解析模板形参
   * @param types
   * @param tp_list
   * @param ast
   * @return
   */
  virtual nlohmann::json parse_template_parms(
      const clang::TemplateParameterList *tp_list);

  /**
   * @brief parse_template_type_parm
   * 解析作为类型使用的类型模板名
   * @param ob
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_template_type_parm(const clang::QualType &type,
                                        nlohmann::json &j);

  /**
   * @brief parse_function
   * 解析函数类型（函数指针的解引用后类型）
   * @param types
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_function_type(const clang::QualType &type,
                                   nlohmann::json &j);

  // ***********************************
  // 全局函数和类方法函数的解析接口

  /**
   * @brief parse_decl_function
   * 解析非本地文件中的类方法或全局函数（非待测仅插桩函数）的报文
   * @return
   */
  virtual nlohmann::json parse_function_declaration(
      const clang::FunctionDecl *func_decl, bool based_on_definition = false);

  /**
   * @brief parse_function_stub
   * 解析函数内部被调用的函数（插桩函数）
   * @param fd
   * @param isDirect
   * @return
   */
  virtual nlohmann::json parse_function_stub(const clang::FunctionDecl *fd
                                             /*bool isDirect*/);

  /**
   * @brief parse_function_method
   * 解析类内方法/构造函数列表
   * @param fd
   * @return
   */
  virtual nlohmann::json parse_function_method(const clang::FunctionDecl *fd);

  /**
   * @brief parse_function_stub
   * 解析函数内部被调用的函数（插桩函数）
   * @param ob
   * @param fd
   * @param ast
   * @param isDirect
   * @return
   */
  virtual void parse_noprototype_function_stub(const clang::FunctionDecl *fd,
                                               const clang::CallExpr *ce,
                                               nlohmann::json &stubs,
                                               bool isDirect);

  virtual void add_using_global(nlohmann::json &use_globals,
                                const std::string &var_name,
                                const nlohmann::json &global,
                                nlohmann::json &test_if);

  /**
   * @brief parse_function_full
   * 解析完整的函数，和函数内部的信息
   * @param func
   * @return
   */
  virtual nlohmann::json parse_function_definition(clang::FunctionDecl *,
                                                   TargetFunction &func);
  // 全局函数和类方法函数的解析接口
  // ***********************************

  virtual void parse_member_usage(clang::MemberExpr *me,
                                  std::string func_space = "");

  virtual void parse_crecord(const clang::QualType &type, nlohmann::json &j);

  virtual void parse_cxxrecord(const clang::CXXRecordDecl *cxxrd,
                               const clang::QualType &type, nlohmann::json &j);

  /**
   * @brief parse_record
   * 解析record，区分c标准和c++标准
   * @param types
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_record(const clang::QualType &type, nlohmann::json &j);

  virtual void parse_enum(const clang::QualType &type, nlohmann::json &j);

  virtual void parse_dependent_type(const clang::QualType &type,
                                    nlohmann::json &j);

  /**
   * @brief parse_reference
   * 解析引用
   * @param types
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_reference(const clang::QualType &type, nlohmann::json &j);

  /**
   * @brief parse_array
   * 解析数组
   * @param types
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_array(const clang::QualType &type, nlohmann::json &j);

  /**
   * @brief parse_pointer
   * 解析指针
   * @param types
   * @param type
   * @param ast
   * @return
   */
  virtual void parse_pointer(const clang::QualType &type, nlohmann::json &j);

  virtual void parse_member_pointer(const clang::QualType &type,
                                    nlohmann::json &j);

  virtual std::string parse_canonical_type(clang::QualType type);

  virtual void parse_fixed_addresses();

  /**
   * @brief 把通过clang得到绝对地址表达式转换为json格式。
   * @param fixed_addresses:
   * clang得到的绝对地址表达式映射，以clang表达式文本为键值。
   * @param ast: clang的AST上下文。
   * @return 绝对地址表达式的json对象集合。
   */
  virtual nlohmann::json parse_fixed_address_exprs(
      const std::map<std::string, std::vector<share::FixedAddress>>
          &fixed_addresses,
      clang::ASTContext &ast);
};

}  // namespace testif
}  // namespace rocket

#endif
